from __future__ import division

from core import *

from .cart import DeliveryCart, FetchCart

 

# TODO: Move some stuff to logistics

class StorageBuilding(Building):
    allowed       = SetAttribute()
    in_storage    = SetAttribute(writable = True)
    fetch_cart    = Attribute(writable = True)
    delivery_cart = Attribute(writable = True)
    
    @property
    def supply(self):
        return self.in_storage - self.allowed
    
    def allow(self, type):
        if type in self._allowed:
            return
        
        self.logistics.remove_building(self)
        self._allowed.add(type)
        self.logistics.add_building(self)
    
    def disallow(self, type):
        if type not in self._allowed:
            return
        
        self.logistics.remove_building(self)
        self._allowed.discard(type)
        self.logistics.add_building(self)
    
    def on_changed(self, goods):
        # FIXME: Probably not working anymore
        if self.storage.stored_types != self.in_storage:
            self.logistics.remove_building(self)
            self.in_storage = self.storage.stored_types
            self.logistics.add_building(self)
    
    def good_sources(self, good):
        return self.logistics.sources_for(good)
    
    def good_sinks(self, good):
        return self.logistics.sinks_for(good)
    
    @property
    def total(self):
        return sum(getattr(self, good) for good in self.good_names)
    
    def day(self, day, month, year):
        if self.fetch_cart is not None and not self.fetch_cart.exists:
            self.fetch_cart = None
        
        if self.delivery_cart is not None and not self.delivery_cart.exists:
            self.delivery_cart = None
        
        if random.random() > self.staffing_level ** 2:
            return
        
        if self.fetch_cart is None:
            self.fetch(*self.source)
            
        if self.delivery_cart is None:
            self.deliver(*self.sink)
    
    def fetch(self, good, source):
        if source is None:
            return
        
        self.fetch_cart = self.spawn(FetchCart, job = source,
                                     destination = source.building, good = good)
    
    def deliver(self, good, target):
        if target is None:
            return
        
        self.delivery_cart = self.spawn(DeliveryCart, job = target,
                                        destination = target.building, good = good)
        target.add_incoming(self.fetch_cart, good(target.quantity))
    
    @property
    def source(self):
        what, whence, best = None, None, None
        
        for good in self.allowed:
            for source in self.good_sources(good):
                if self.storage.free_storage_for(source.good_type) <= 0:
                    continue
                
                num = source.quantity
                
                if num <= 0:
                    continue
                
                score = num / (source.building.position.distance(self.position) ** 1.1 + 10)
                
                if best is None or score > best:
                    what   = source.good_type
                    whence = source
                    best   = score
        
        if best is None or best < 1:
            return None, None
        else:
            return what, whence
    
    @property
    def sink(self):
        what, whence, best = None, None, None
        
        for good in self.storage.stored_types:
            for sink in self.good_sinks(good):
                if self.storage.number_of(sink.good_type) <= 0:
                    continue
            
                num = min(sink.quantity,
                          self.storage.number_of(sink.good_type)) * sink.building.urgency_of(sink.good_type)
                
                if num <= 0:
                    continue
                
                score = num / (sink.building.position.distance(self.position) + 10)
                
                if best is None or score > best:
                    what   = sink.good_type
                    whence = sink
                    best   = score
        
        return what, whence
    
    @property
    def spawn_point(self):
        tiles = list(self.area.adjacent_with(lambda position: self.city.map.at(position).is_walkable))
        
        if tiles:
            return random.choice(tiles)
        else:
            return None
